package br.org.bertol.mestrado.engine.optimisation.sls;

import java.util.Collections;

import org.apache.log4j.Logger;

import br.org.bertol.mestrado.Verify;
import br.org.bertol.mestrado.engine.optimisation.Unit;
import br.org.bertol.mestrado.engine.optimisation.moo.ParetoDominace;

public final class VariableNeighborhoodSearch<U extends Unit> extends
        StochasticLocalSearch<U> {

    private final int maxNeighborhood;

    public VariableNeighborhoodSearch(final Verify verify, final U startUnit,
        final long numIteracoes) {
        super(verify, numIteracoes, startUnit);

        this.maxNeighborhood = this.verify.getClasses().size() / 2;

    }

    @SuppressWarnings("unchecked")
    @Override
    public void evaluate() throws Exception {

        betterUnit = (U) startUnit.clone();

        Logger.getLogger(getClass()).info("ILS START : " + startUnit);

        int vns_control = 1;

        int num_iteracoes = 0;

        int bestIeration = 0;

        // loop principal
        while (num_iteracoes - bestIeration < numIteracoes) {
            num_iteracoes++;

            vns_control = 1;

            while (vns_control <= maxNeighborhood) {

                U candidato_s = (U) betterUnit.clone();

                generateKesimoNeighbor(candidato_s, vns_control);

                // realiza verifica��o do caminho para ver esta entrada �
                // poss�vel
                // verifica��o necess�ria para diversifica��o
                verify.verifyStrongConstraint(candidato_s, true);

                if (candidato_s.isValidPath()) {

                    hillClimbing.setPath(candidato_s);

                    hillClimbing.runHillClimbing();

                    final U candidato_ss = hillClimbing.getBestPath();

                    if (candidato_s.isValidPath()
                            && paretoDominace.compare(candidato_ss.getHeight(),
                                                      candidato_s.getHeight()) != ParetoDominace.RIGHT_DOMINE) {

                        betterUnit = (U) candidato_ss.clone();

                        candidato_s = (U) candidato_ss.clone();

                        bestIeration = num_iteracoes;

                        vns_control = 1;

                        Logger.getLogger(getClass())
                                .debug(
                                       "Caminho melhor encontrado => "
                                + candidato_ss);

                    } else
                        vns_control++;

                    hillClimbing.reset();
                }
            }
        }

        Logger.getLogger(getClass()).info("VNS STOP : " + betterUnit);

    }

    private void generateKesimoNeighbor(final U candidato_s, final int k) {

        for (int i = 0; i < k; i++) {

            int x1 = (int) (Math.random() * verify.getClasses().size());

            int x2 = x1;

            while (x1 == x2)
                x2 = (int) (Math.random() * verify.getClasses().size());

            Collections.swap(candidato_s.getRoute(), x1, x2);
        }
    }

}
